package com.mjlf.timer.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.junit.jupiter.api.Test;

/**
 * @ClassName Server
 * @auther mjlf
 * @Date 2019/9/23 9:36
 * @Description netty服务端
 */
public class Server {

    public static void main(String[] args) throws InterruptedException {
        // 用于服务端接受客户端连接
        // NioEventLoopGroup 继承 MultithreadEventLoopGroup
        // 会在内部初始化Selector, 选择策略， 拒绝策略
        // SingleThreadEventExecutor 内存创建线程池
        EventLoopGroup boss = new NioEventLoopGroup();

        //用于进行SocketChannel网络读写
        EventLoopGroup work = new NioEventLoopGroup();

        try {
            //netty用于启动NIO服务器的辅助启动类
            ServerBootstrap bootstrap = new ServerBootstrap();

            //将boss 赋值给父类 AbstractBootstrap 的group
            //将work 赋值给自己的childGroup
            bootstrap.group(boss, work)
                    //首先将 NioServerSocketChannel.class 封装到 ReflectiveChannelFactory，
                    //然后将ReflectiveChannelFactory对象赋值给channelFactory， 后续再bind()方法中会通过channelFactory
                    //反射方法将NioServerSocketChannel实例创建出来
                    .channel(NioServerSocketChannel.class)
                    //将值放到option Map中
                    .option(ChannelOption.SO_BACKLOG, 1024)

                    //设置childHandler, io轮询/数据读写等handler
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel arg0) throws Exception{
                            //双向链表
                            arg0.pipeline()
                                    //行解析器， 当遇到换行符的时候就认为是一个完成的报文，如果遇到长度超过1024还没有换行符，
                                    //那么将会抛出异常
                                    .addLast(new LineBasedFrameDecoder(1024))
                                    .addLast(new StringEncoder())
                                    .addLast(new StringDecoder())
                                    .addLast(new TimeServerHandler());
                        }
                    });
            //绑定端口返回ChannelFuture，用于异步处理
            ChannelFuture future = bootstrap.bind(9090).sync();
            //等待服务端链路关闭后main函数才退出
            future.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }

    static class TimeServerHandler extends SimpleChannelInboundHandler<String> {

        public int count = 0;
        @Override
        public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg);

            System.out.println("统计数量：" + (++count));

            ByteBuf byteBuf1 = Unpooled.copiedBuffer(("xxxx"+System.getProperty("line.separator")).getBytes());
            ctx.writeAndFlush(byteBuf1);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.flush();
        }
    }
}
